﻿namespace HZY.Framework.Core.AspNetCore;

/// <summary>
/// 启动模块
/// </summary>
/// <typeparam name="TStartupModule"></typeparam>
public abstract class StartupModule<TStartupModule> : IStartupModule where TStartupModule : IStartupModule, new()
{
    private static object _lockObj = new object();

    /// <summary>
    /// 启动器执行循序，从小到大的顺序执行
    /// </summary>
    public virtual int Order { get; set; } = 328;

    /// <summary>
    /// 程序启动器
    /// </summary>
    public StartupModule()
    {
        var startupType = this.GetType();

        if (startupType.FullName != typeof(TStartupModule).FullName)
        {
            throw new Exception($"当前实例类型 {startupType.FullName} 与 泛型传入类型 {typeof(TStartupModule).FullName} 不一致!");
        }
    }

    /// <summary>
    /// 配置服务
    /// </summary>
    /// <param name="webApplicationBuilder"></param>
    public virtual void ConfigureServices(WebApplicationBuilder webApplicationBuilder)
    {
    }

    /// <summary>
    /// 配置
    /// </summary>
    /// <param name="webApplication"></param>
    public virtual void Configure(WebApplication webApplication)
    {
    }

    /// <summary>
    /// 程序启动 可以获取程序启动 urls
    /// </summary>
    /// <param name="webApplication"></param>
    public virtual void ApplicationStarted(WebApplication webApplication)
    {
    }

    /// <summary>
    /// 程序停止中
    /// </summary>
    /// <param name="webApplication"></param>
    public virtual void ApplicationStopping(WebApplication webApplication)
    {
    }

    /// <summary>
    /// 程序已经停止
    /// </summary>
    /// <param name="webApplication"></param>
    public virtual void ApplicationStopped(WebApplication webApplication)
    {
    }

    /// <summary>
    /// 获取所有导入的启动器实例
    /// </summary>
    /// <returns></returns>
    public List<IStartupModule> GetAllImportStartup()
    {
        lock (_lockObj)
        {
            var startupType = this.GetType();

            CheckLoopImportStartup(startupType);

            var importStartupAttributes = startupType.GetCustomAttributes<ImportStartupModuleAttribute>();

            var startups = new List<IStartupModule> { this };

            foreach (var item in importStartupAttributes)
            {
                FindImportStartup(startups, item);
            }

            foreach (var item in startups.ToList())
            {
                if (startups.Count(w => w.GetType().FullName == item.GetType().FullName) > 1)
                {
                    startups.RemoveAt(startups.IndexOf(item));
                }
            }

            return startups.OrderBy(w => w.Order).ToList();
        }
    }

    #region 私有

    /// <summary>
    /// 查找导入启动器特性
    /// </summary>
    /// <param name="startups"></param>
    /// <param name="importStartupAttribute"></param>
    private void FindImportStartup(List<IStartupModule> startups, ImportStartupModuleAttribute importStartupAttribute)
    {
        foreach (var startup in importStartupAttribute.GetStartups())
        {
            if (startups.Any(w => w.GetType().FullName == startup.GetType().FullName)) continue;

            startups.Add(startup);
        }

        // 循环启动器类型
        foreach (var startupType in importStartupAttribute.GetStartupTypes())
        {
            CheckLoopImportStartup(startupType);

            // 循环类型上的启动器标记
            var importStartupAttributes = startupType.GetCustomAttributes<ImportStartupModuleAttribute>();

            foreach (var item in importStartupAttributes)
            {
                FindImportStartup(startups, item);
            }
        }
    }

    /// <summary>
    /// 验证循环导入启动器类型
    /// </summary>
    /// <param name="startupType"></param>
    private void CheckLoopImportStartup(Type startupType)
    {
        // 循环类型上的启动器标记
        var importStartupAttributes = startupType.GetCustomAttributes<ImportStartupModuleAttribute>();

        foreach (var item in importStartupAttributes)
        {
            // 验证启动器类型 是否被循环导入了 ImportStartupAttribute 中
            if (item.GetStartupTypes().Any(w => w.FullName == startupType.FullName))
            {
                throw new Exception(
                    $"错误的导入。类型 {startupType.FullName} 特性标记 [ImportStartup] 不允许导入自身 {startupType.FullName} 启动器类型！");
            }
        }
    }

    #endregion
}